package org.apache.dolphinscheduler.api.service.impl;

import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.dolphinscheduler.api.dto.model.ModelDto;
import org.apache.dolphinscheduler.api.dto.model.PublishModelDto;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.service.ModelFileService;
import org.apache.dolphinscheduler.api.service.ModelService;
import org.apache.dolphinscheduler.api.service.UserGroupService;
import org.apache.dolphinscheduler.api.service.UsersService;
import org.apache.dolphinscheduler.api.utils.PageInfo;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.dao.entity.Model;
import org.apache.dolphinscheduler.dao.entity.ModelFile;
import org.apache.dolphinscheduler.dao.entity.ModelProfile;
import org.apache.dolphinscheduler.dao.entity.ModelVersion;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.ModelMapper;
import org.apache.dolphinscheduler.dao.mapper.ModelProfileMapper;
import org.apache.dolphinscheduler.dao.mapper.ModelVersionMapper;
import org.apache.dolphinscheduler.dao.mapper.UserMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

@Service
public class ModelServiceImpl extends BaseServiceImpl implements ModelService {
	@Autowired
	private ModelMapper modelMapper;
	@Autowired
	private ModelProfileMapper modelProfileMapper;
	@Autowired
	private ModelVersionMapper modelVersionMapper;
	@Autowired
	private ModelFileService modelFileService;
	@Autowired
	private UsersService usersService;
	@Autowired
	private UserGroupService userGroupService;
	
	public Result<Object> queryModelByPaging(User loginUser, ModelDto dto) {
		Page<Model> page = new Page<>(dto.getPageNo(), dto.getPageSize());
		QueryWrapper<Model> qw = new QueryWrapper<Model>();
		if(dto.getQueryType() == 1) {
			qw.and(warrper -> warrper.eq("uid", loginUser.getId()).or().eq("is_public", 0));
		} else if(dto.getQueryType() == 2) {
			qw.eq("uid", loginUser.getId());
		} else if(dto.getQueryType() == 3) {
			qw.eq("is_public", 0);
		} else if(dto.getQueryType() == 4) {
			// 查询登录用户的用户组及用户组内的成员是否有共享的模型和数据集
			List<Integer> userIds = userGroupService.queryAllUserByGroupAdmin(loginUser);
			if(userIds != null && userIds.size() > 0) {
				qw.and(warrper -> warrper.in("uid", userIds).eq("is_public", 2));
			}
		}
		if(StringUtils.isNotBlank(dto.getName())) {
			qw.like("name", "%" + dto.getName() + "%");
		}
		if(dto.getTypeId() != null) {
			qw.eq("type_id", dto.getTypeId());
		}
		if(dto.getModelType() != null) {
			qw.eq("model_type", dto.getModelType());
		}
		Page<Model> p = modelMapper.selectPage(page, qw);
		PageInfo<Model> pageInfo = new PageInfo<>(dto.getPageNo(), dto.getPageSize());
		pageInfo.setTotal((int)p.getTotal());
		if(p.getRecords() != null && p.getRecords().size() > 0) {
			p.getRecords().forEach(m -> {
				if(m.getUid() != null) {
					User user = usersService.queryUser(m.getUid());
					if(user != null) {
						m.setUserName(user.getUserName());
					}
				}
			});
		}
        pageInfo.setTotalList(p.getRecords());
		return Result.success(pageInfo);
	}

	public Result<Object> createModel(User loginUser, Model model) {
		if(model == null) {
			return Result.error();
		}
		model.setUid(loginUser.getId());
		model.setStatus(1);
		model.setCreateTime(new Date());
		int count = modelMapper.insert(model);
		if(count <= 0) {
			return Result.error();
		}
		return Result.success(model);
	}
	
	public Result<Object> updateModel(User loginUser, Integer modelId, Model model) {
		Model _model = modelMapper.selectById(modelId);
		if(_model == null) {
			return Result.error();
		}
		_model.setUpdateTime(new Date());
		_model.setName(model.getName());
		_model.setRemark(model.getRemark());
		_model.setTypeId(model.getTypeId());
		_model.setFramework(model.getFramework());
		_model.setIsPublic(model.getIsPublic());
		int count = modelMapper.updateById(_model);
		if(count <= 0) {
			return Result.error();
		}
		return Result.success(_model);
	}
	
	public Result<Object> deleteModel(User loginUser, Integer modelId) {
		Model _model = modelMapper.selectById(modelId);
		if(_model == null) {
			return Result.error();
		}
		if(!isAdmin(loginUser) && _model.getUid() != loginUser.getId()) {
			return Result.error();
		}
		deleteVersion(modelId, null);
		deleteProfile(modelId, null);
		modelMapper.deleteById(modelId);
		
		return Result.success();
	}
	
	public Result<Object> createProfile(User loginUser, Integer modelId, ModelProfile profile) {
		Model model = modelMapper.selectById(modelId);
		if(model == null) {
			return Result.error();
		}
		deleteProfile(modelId, null);
		profile.setModelId(modelId);
		profile.setCreateTime(new Date());
		int count = modelProfileMapper.insert(profile);
		if(count > 0) {
			return Result.success(profile);
		}
		return Result.error();
	}
	
	public Result<Object> getProfile(User loginUser, Integer modelId) {
		Model model = modelMapper.selectById(modelId);
		if(model == null) {
			return Result.error();
		}
		QueryWrapper<ModelProfile> qw = new QueryWrapper<ModelProfile>();
		qw.eq("model_id", modelId);
		qw.orderByDesc("create_time");
		qw.last("LIMIT 1");
		ModelProfile profile = modelProfileMapper.selectOne(qw);
		return Result.success(profile);
	}
	
	public Result<Object> createVersion(User loginUser, Integer modelId, ModelVersion version) {
		Model model = modelMapper.selectById(modelId);
		if(model == null) {
			return Result.error();
		}
		version.setModelId(modelId);
		version.setCreateTime(new Date());
		version.setStatus(1);
		int count = modelVersionMapper.insert(version);
		if(count > 0) {
			return Result.success(version);
		}
		return Result.error();
	}
	
	public Result<Object> listVersion(User loginUser, Integer modelId) {
		Model model = modelMapper.selectById(modelId);
		if(model == null) {
			return Result.error();
		}
		QueryWrapper<ModelVersion> qw = new QueryWrapper<ModelVersion>();
		qw.eq("model_id", modelId);
		qw.orderByDesc("create_time");
		List<ModelVersion> list = modelVersionMapper.selectList(qw);
		return Result.success(list);
	}
	
	public Result<Object> updateProfile(User loginUser, Integer profileId, ModelProfile profile) {
		ModelProfile _profile = modelProfileMapper.selectById(profileId);
		if(_profile == null) {
			return Result.error();
		}
		if(StringUtils.isBlank(profile.getContent())) {
			return Result.error();
		}
		_profile.setContent(profile.getContent());
		_profile.setUpdateTime(new Date());
		int count = modelProfileMapper.updateById(_profile);
		if(count > 0) {
			return Result.success(_profile);
		}
		return Result.error();
	}
	
	public Result<Object> publish(User loginUser, PublishModelDto dto) {
		if(StringUtils.isBlank(dto.getVersion()) || dto.getModelId() == null) {
			return Result.error();
		}
		Model _model = modelMapper.selectById(dto.getModelId());
		if(_model == null) {
			return Result.error();
		}
		if(_model.getIsPublic() == 1) {
			return Result.error(Status.PRIVATE_MODEL_NOT_PUBLISH);
		}
		// 查询模型名称和版本是否已发布过
		Long count = countModelByNameVersion(_model.getName(), dto.getVersion());
		if(count > 0) {
			return Result.error(Status.MODEL_VERSION_IS_EXIST);
		}
		// 判断非管理员只能发布自己的模型
		if(!isAdmin(loginUser) && loginUser.getId() != _model.getUid()) {
			return Result.error();
		}
		// 发布模型，需要将模型的主记录与关联文件记录复制一条新数据
		ModelProfile _profile = queryModelProfile(dto.getModelId());
		ModelVersion _version = queryModelVersion(dto.getModelId(), dto.getVersion());
		if(_version == null) {
			return Result.error(Status.MODEL_VERSION_NOT_EXIST);
		}
		List<ModelFile> listModelFile = modelFileService.queryModelFileByModelId(dto.getModelId(), -1);
		if(listModelFile == null) {
			return Result.error(Status.PUBLISH_MODEL_NOT_FILE);
		} else {
			if(listModelFile.size() == 0) {
				return Result.error(Status.PUBLISH_MODEL_NOT_FILE);
			}
		}
		Model model = new Model();
		BeanUtils.copyProperties(_model, model);
		model.setId(null);
		model.setVersion(dto.getVersion());
		model.setStatus(2);
		model.setCreateTime(new Date());
		int num = modelMapper.insert(model);
		if(num > 0) {
			// 修改当前版本的状态为已发布
			_version.setStatus(2);
			modelVersionMapper.updateById(_version);
			
			Integer modelId = model.getId();
			if(_profile != null) {
				ModelProfile profile = new ModelProfile();
				BeanUtils.copyProperties(_profile, profile);
				profile.setId(null);
				profile.setModelId(modelId);
				profile.setCreateTime(new Date());
				modelProfileMapper.insert(profile);
			}
			
			ModelVersion version = new ModelVersion();
			BeanUtils.copyProperties(_version, version);
			version.setId(null);
			version.setModelId(modelId);
			version.setStatus(2);
			version.setCreateTime(new Date());
			modelVersionMapper.insert(version);
			
			saveModelFile(listModelFile, -1, modelId);
		}
		return Result.success();
	}
	
	public Result<Object> unPublish(User loginUser, Integer modelId) {
		Model _model = modelMapper.selectById(modelId);
		if(_model == null) {
			return Result.error();
		}
		if(!isAdmin(loginUser) && _model.getUid() != loginUser.getId()) {
			return Result.error();
		}
		// 修改模型状态为未发布
		_model.setStatus(1);
		_model.setUpdateTime(new Date());
		modelMapper.updateById(_model);
		QueryWrapper<ModelVersion> qw = new QueryWrapper<ModelVersion>();
		qw.eq("model_id", modelId);
		List<ModelVersion> versionList = modelVersionMapper.selectList(qw);
		if(versionList != null && versionList.size() > 0) {
			versionList.forEach(version -> {
				version.setStatus(1);
				modelVersionMapper.updateById(version);
			});
		}
		return Result.success();
	}
	
	private void saveModelFile(List<ModelFile> listModelFile, Integer pid, Integer modelId) {
		for(ModelFile mf : listModelFile) {
			ModelFile _file = new ModelFile();
			BeanUtils.copyProperties(mf, _file);
			_file.setId(null);
			_file.setModelId(modelId);
			_file.setCreateTime(new Date());
			_file.setPid(pid);
			modelFileService.save(_file);
			if(mf.getChildFile() != null && mf.getChildFile().size() > 0) {
				saveModelFile(mf.getChildFile(), _file.getId(), modelId);
			}
		}
	}
	
	private Long countModelByNameVersion(String name, String version) {
		QueryWrapper<Model> qw = new QueryWrapper<Model>();
		qw.eq("name", name);
		qw.eq("version", version);
		qw.eq("status", 2);
		return modelMapper.selectCount(qw);
	}
	
	private void deleteProfile(Integer modelId, Integer profileId) {
		QueryWrapper<ModelProfile> qw = new QueryWrapper<ModelProfile>();
		if(modelId != null) {
			qw.eq("model_id", modelId);
		}
		if(profileId != null) {
			qw.eq("id", profileId);
		}
		modelProfileMapper.delete(qw);
	}
	
	private void deleteVersion(Integer modelId, Integer versionId) {
		QueryWrapper<ModelVersion> qw = new QueryWrapper<ModelVersion>();
		if(modelId != null) {
			qw.eq("model_id", modelId);
		}
		if(versionId != null) {
			qw.eq("id", versionId);
		}
		modelVersionMapper.delete(qw);
	}
	
	private ModelProfile queryModelProfile(Integer modelId) {
		QueryWrapper<ModelProfile> qw = new QueryWrapper<ModelProfile>();
		qw.eq("model_id", modelId);
		qw.last("LIMIT 1");
		return modelProfileMapper.selectOne(qw);
	}
	
	private ModelVersion queryModelVersion(Integer modelId, String version) {
		QueryWrapper<ModelVersion> qw = new QueryWrapper<ModelVersion>();
		qw.eq("model_id", modelId);
		qw.eq("version", version);
		qw.last("LIMIT 1");
		return modelVersionMapper.selectOne(qw);
	}
}
